package cn.cdiothree.service.impl;

import cn.cdiothree.dao.AuthMapper;
import cn.cdiothree.dao.UserMapper;
import cn.cdiothree.domain.auth.ResponseUserToken;
import cn.cdiothree.domain.auth.Role;
import cn.cdiothree.domain.auth.User;
import cn.cdiothree.domain.auth.UserDetail;
import cn.cdiothree.enums.MessageEnum;
import cn.cdiothree.exception.UserException;
import cn.cdiothree.service.AuthService;
import cn.cdiothree.util.JwtUtils;
import cn.cdiothree.util.OnlineUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.DisabledException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;

import java.util.Date;

/**
 * 用户登录等操作服务类
 * @author cumthing
 */
@Service
@Slf4j
public class AuthServiceImpl implements AuthService {
    private final AuthenticationManager authenticationManager;
    private final UserDetailsService userDetailsService;
    private final JwtUtils jwtTokenUtil;
    private final AuthMapper authMapper;



    @Value("${jwt.tokenHead}")
    private String tokenHead;

    @Autowired
    public AuthServiceImpl(AuthenticationManager authenticationManager, @Qualifier("CustomUserDetailsService") UserDetailsService userDetailsService, JwtUtils jwtTokenUtil, AuthMapper authMapper, UserMapper userMapper, OnlineUtil onlineUtil) {
        this.authenticationManager = authenticationManager;
        this.userDetailsService = userDetailsService;
        this.jwtTokenUtil = jwtTokenUtil;
        this.authMapper = authMapper;

        this.onlineUtil = onlineUtil;
    }

    @Autowired
    OnlineUtil onlineUtil;

    /**
     * 用户注册
     * @param userDetail
     * @return
     */
    @Override
    public UserDetail register(UserDetail userDetail) {
        log.info("进行用户注册操作，用户名为{}",userDetail.getUsername());
        final String username = userDetail.getUsername();
        if(authMapper.findByUsername(username)!=null) {
            throw new UserException(MessageEnum.USER_EXIST);
        }
        BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
        final String rawPassword = userDetail.getPassword();
        userDetail.setPassword(encoder.encode(rawPassword));
        userDetail.setLastPasswordResetDate(new Date());

        authMapper.insert(userDetail);
        long roleId = userDetail.getRole().getId();
        Role role = authMapper.findRoleById(roleId);
        userDetail.setRole(role);
        authMapper.insertRole(userDetail.getId(), roleId);
        return userDetail;
    }

    /**
     * 用户登录
     * @param username
     * @param password
     * @return
     */
    @Override
    public ResponseUserToken login(String username, String password) {
        log.info("用户{}进行登录操作",username);
        //用户验证
        final Authentication authentication = authenticate(username, password);
        //存储认证信息
        SecurityContextHolder.getContext().setAuthentication(authentication);
        //生成token
        final UserDetail userDetail = (UserDetail) authentication.getPrincipal();
        final String token = jwtTokenUtil.generateAccessToken(userDetail);

        jwtTokenUtil.putToken(username, token);
        onlineUtil.setLastOperateTimeByUsername(username);
        return new ResponseUserToken(token, userDetail);

    }

    /**
     * 用户登出
     * @param token
     */
    @Override
    public void logout(String token) {
        log.info("用户进行登出操作");
        token = token.substring(tokenHead.length());
        String userName = jwtTokenUtil.getUsernameFromToken(token);
        jwtTokenUtil.deleteToken(userName);
    }

    /**
     * 用户刷新
     * @param oldToken
     * @return
     */
    @Override
    public ResponseUserToken refresh(String oldToken) {
        log.info("用户{}进行刷新操作");
        String token = oldToken.substring(tokenHead.length());
        String username = jwtTokenUtil.getUsernameFromToken(token);
        UserDetail userDetail = (UserDetail) userDetailsService.loadUserByUsername(username);

        if (jwtTokenUtil.canTokenBeRefreshed(token, userDetail.getLastPasswordResetDate())){
            token =  jwtTokenUtil.refreshToken(token);
            return new ResponseUserToken(token, userDetail);
        }
        return null;
    }

    /**
     * 获取用户token
     * @param token
     * @return
     */
    @Override
    public UserDetail getUserByToken(String token) {
        log.info("获取用户token",token);
        token = token.substring(tokenHead.length());
        return jwtTokenUtil.getUserFromToken(token);
    }

    private Authentication authenticate(String username, String password) {
        try {
            //该方法会去调用userDetailsService.loadUserByUsername()去验证用户名和密码，如果正确，则存储该用户名密码到“security 的 context中”
            return authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(username, password));
        } catch (DisabledException | BadCredentialsException e) {
            throw new UserException(MessageEnum.USER_INVALID);
        }
    }
}
